package cn.dengta.context.rpc;

import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import cn.dengta.context.web.RequestContextImpl;
import cn.dengta.webapp.base.util.MDCUtil;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.JSONUtil;
import me.codeplayer.util.StringUtil;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metadata.MetadataService;
import org.apache.dubbo.rpc.*;

/**
 * Dubbo 消费者/生产者通用过滤器，在 调用提供者服务 或 被消费者调用 时触发
 */
@Activate(group = { CommonConstants.PROVIDER, CommonConstants.CONSUMER })
@Slf4j
public class DubboSessionFilter implements Filter {

	static final String SESSION_ID = "springSessionId";
	static final String INVOKE_ID = "invokeId";
	static AtomicLong counter = new AtomicLong();

	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		final URL uri = invoker.getUrl();
		if (MetadataService.isMetadataService(uri.getServiceInterface())) {
			return invoker.invoke(invocation);
		}
		// 并不要求完全唯一，只要能够方便定位即可
		String invokeId;
		RpcContext context = RpcContext.getServiceContext();
		final RequestContextImpl req = RequestContextImpl.get();
		final boolean isConsumer = uri.getSide(CommonConstants.PROVIDER_SIDE).equals(CommonConstants.CONSUMER_SIDE); // 消费者端
		if (isConsumer) { // context.isConsumerSide() 在某些 dubbo 版本可能有 NPE 问题
			invokeId = System.currentTimeMillis() + "_" + counter.incrementAndGet();
			log.info("BEGIN: invokeId={} | {}", invokeId, uri);
			String sessionId = req.getSessionId();
			if (StringUtil.isEmpty(sessionId)) {
				HttpServletRequest request = req.getRequest();
				if (request != null) { // 来自 Web 端的原始请求
					HttpSession session = request.getSession();
					req.getSessionContext(request);
					req.setSessionId(sessionId = session.getId()); // 如果有多次调用，无需重复设置
					req.flushSession(true);
				}
			}
			context.setAttachment(SESSION_ID, sessionId);
			context.setAttachment(INVOKE_ID, invokeId);
			context.setAttachment("trace_id", MDCUtil.getTraceId());
//			RpcContextUtil.init(null, req.sessionLanguage(), req.getAppId());
		} else { // 生产者端
			String sessionId = context.getAttachment(SESSION_ID);
			req.tryClean(); // 保证初始化数据是干净的
			req.setSessionId(sessionId);
			invokeId = context.getAttachment(INVOKE_ID);
		}
		req.flushSession(false);
		try {
			return invoker.invoke(invocation);
		} finally {
			log.info("END: invokeId={}", invokeId);
			req.flushSession(false);
		}
	}

}
